/*------------------------------------------------------------------------------
 * MDK Middleware - Component ::USB:Device
 * Copyright (c) 2004-2024 Arm Limited (or its affiliates). All rights reserved.
 *------------------------------------------------------------------------------
 * Name:    usbd_cmsis_rtos2.c
 * Purpose: USB Device (USBD) - RTOS abstraction implemented on CMSIS-RTOS2
 *----------------------------------------------------------------------------*/

#include "usbd_cmsis_rtos2.h"

#include <string.h>

#include "usbd_compatibility.h"

// Local structure definitions
typedef struct {
  uint32_t       stack_size;    // size of thread stack
  osPriority_t   priority;      // thread priority
} thread_sp_t;

// Resources definition

// Import thread function prototypes
extern void USBD_Core_Thread           (void *arg);
extern void USBD_ADC_Thread            (void *arg);
extern void USBD_CDC_Int_Thread        (void *arg);
extern void USBD_CDC_Bulk_Thread       (void *arg);
extern void USBD_HID_Thread            (void *arg);
extern void USBD_MSC_Thread            (void *arg);
extern void USBD_CustomClass_EP_Thread (void *arg);

// Import thread definitions generated by each Device instance
// in usbd_config_dev_n.c file (n = 0 .. 3)
#ifdef RTE_USB_Device_0
extern const osThreadAttr_t usbd0_core_thread_attr;
#endif
#ifdef RTE_USB_Device_1
extern const osThreadAttr_t usbd1_core_thread_attr;
#endif
#ifdef RTE_USB_Device_2
extern const osThreadAttr_t usbd2_core_thread_attr;
#endif
#ifdef RTE_USB_Device_3
extern const osThreadAttr_t usbd3_core_thread_attr;
#endif

static const osThreadAttr_t * const usbd_core_thread_attr[USBD_DEV_NUM] = {
#ifdef RTE_USB_Device_0
    &usbd0_core_thread_attr
#endif
#ifdef RTE_USB_Device_1
  , &usbd1_core_thread_attr
#endif
#ifdef RTE_USB_Device_2
  , &usbd2_core_thread_attr
#endif
#ifdef RTE_USB_Device_3
  , &usbd3_core_thread_attr
#endif
};

// Create thread definitions for classes requiring threads
#if   (USBD_CUSTOM_CLASS_NUM > 0)
#define USBD_CCn_EPx_THREAD_SP(n,x)             {((((USBD_CUSTOM_CLASS##n##_EP##x##_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP##x##_EN), \
                                                    (USBD_CUSTOM_CLASS##n##_EP##x##_THREAD_PRIORITY)}
#define USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(n)  (((((USBD_CUSTOM_CLASS##n##_EP1_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP1_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP2_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP2_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP3_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP3_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP4_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP4_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP5_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP5_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP6_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP6_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP7_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP7_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP8_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP8_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP9_THREAD_STACK_SIZE  + 7U) / 8U) * 8U) * USBD##n##_EP9_EN)  + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP10_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP10_EN) + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP11_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP11_EN) + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP12_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP12_EN) + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP13_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP13_EN) + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP14_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP14_EN) + \
                                                 ((((USBD_CUSTOM_CLASS##n##_EP15_THREAD_STACK_SIZE + 7U) / 8U) * 8U) * USBD##n##_EP15_EN))

#define USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0     (1U)
#ifdef  RTE_USB_Device_CustomClass_0
#if    (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(0) != 0U)
#undef  USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0
#define USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0     (0U)
#endif
#endif
#ifdef  RTE_USB_Device_CustomClass_1
#if    (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(1) != 0U)
#undef  USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0
#define USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0     (0U)
#endif
#endif
#ifdef  RTE_USB_Device_CustomClass_2
#if    (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(2) != 0U)
#undef  USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0
#define USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0     (0U)
#endif
#endif
#ifdef  RTE_USB_Device_CustomClass_3
#if    (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(3) != 0U)
#undef  USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0
#define USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0     (0U)
#endif
#endif

static const char *usbd_cc_ep_thread_name          [USBD_CUSTOM_CLASS_NUM * USBD_EP_NUM] = {
#ifdef RTE_USB_Device_CustomClass_0
    ""
#if (USBD_EP_LAST_NUM >= 1)
  , "USBD_CustomClass0_EP1_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 2)
  , "USBD_CustomClass0_EP2_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 3)
  , "USBD_CustomClass0_EP3_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 4)
  , "USBD_CustomClass0_EP4_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 5)
  , "USBD_CustomClass0_EP5_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 6)
  , "USBD_CustomClass0_EP6_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 7)
  , "USBD_CustomClass0_EP7_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 8)
  , "USBD_CustomClass0_EP8_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 9)
  , "USBD_CustomClass0_EP9_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 10)
  , "USBD_CustomClass0_EP10_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 11)
  , "USBD_CustomClass0_EP11_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 12)
  , "USBD_CustomClass0_EP12_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 13)
  , "USBD_CustomClass0_EP13_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 14)
  , "USBD_CustomClass0_EP14_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 15)
  , "USBD_CustomClass0_EP15_Thread"
#endif
#endif
#ifdef RTE_USB_Device_CustomClass_1
  , ""
#if (USBD_EP_LAST_NUM >= 1)
  , "USBD_CustomClass1_EP1_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 2)
  , "USBD_CustomClass1_EP2_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 3)
  , "USBD_CustomClass1_EP3_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 4)
  , "USBD_CustomClass1_EP4_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 5)
  , "USBD_CustomClass1_EP5_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 6)
  , "USBD_CustomClass1_EP6_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 7)
  , "USBD_CustomClass1_EP7_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 8)
  , "USBD_CustomClass1_EP8_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 9)
  , "USBD_CustomClass1_EP9_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 10)
  , "USBD_CustomClass1_EP10_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 11)
  , "USBD_CustomClass1_EP11_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 12)
  , "USBD_CustomClass1_EP12_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 13)
  , "USBD_CustomClass1_EP13_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 14)
  , "USBD_CustomClass1_EP14_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 15)
  , "USBD_CustomClass1_EP15_Thread"
#endif
#endif
#ifdef RTE_USB_Device_CustomClass_2
  , ""
#if (USBD_EP_LAST_NUM >= 1)
  , "USBD_CustomClass2_EP1_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 2)
  , "USBD_CustomClass2_EP2_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 3)
  , "USBD_CustomClass2_EP3_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 4)
  , "USBD_CustomClass2_EP4_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 5)
  , "USBD_CustomClass2_EP5_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 6)
  , "USBD_CustomClass2_EP6_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 7)
  , "USBD_CustomClass2_EP7_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 8)
  , "USBD_CustomClass2_EP8_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 9)
  , "USBD_CustomClass2_EP9_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 10)
  , "USBD_CustomClass2_EP10_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 11)
  , "USBD_CustomClass2_EP11_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 12)
  , "USBD_CustomClass2_EP12_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 13)
  , "USBD_CustomClass2_EP13_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 14)
  , "USBD_CustomClass2_EP14_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 15)
  , "USBD_CustomClass2_EP15_Thread"
#endif
#endif
#ifdef RTE_USB_Device_CustomClass_3
  , ""
#if (USBD_EP_LAST_NUM >= 1)
  , "USBD_CustomClass3_EP1_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 2)
  , "USBD_CustomClass3_EP2_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 3)
  , "USBD_CustomClass3_EP3_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 4)
  , "USBD_CustomClass3_EP4_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 5)
  , "USBD_CustomClass3_EP5_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 6)
  , "USBD_CustomClass3_EP6_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 7)
  , "USBD_CustomClass3_EP7_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 8)
  , "USBD_CustomClass3_EP8_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 9)
  , "USBD_CustomClass3_EP9_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 10)
  , "USBD_CustomClass3_EP10_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 11)
  , "USBD_CustomClass3_EP11_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 12)
  , "USBD_CustomClass3_EP12_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 13)
  , "USBD_CustomClass3_EP13_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 14)
  , "USBD_CustomClass3_EP14_Thread"
#endif
#if (USBD_EP_LAST_NUM >= 15)
  , "USBD_CustomClass3_EP15_Thread"
#endif
#endif
};

#ifdef RTE_CMSIS_RTOS2_RTX5
static osRtxThread_t      usbd_cc_ep_thread_cb_mem [USBD_CUSTOM_CLASS_NUM * USBD_EP_NUM] __attribute__((section(".bss.os.thread.cb")));
static uint64_t           usbd_cc_ep_thread_stack_mem[
#if   (USBD_CC_EP_THREAD_TOTAL_STACK_SIZE_IS_0 == 1U)
                                                      1U
#else
#ifdef RTE_USB_Device_CustomClass_0
                                                      (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(0) / 8U) 
#endif
#ifdef RTE_USB_Device_CustomClass_1
                                                    + (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(1) / 8U) 
#endif
#ifdef RTE_USB_Device_CustomClass_2
                                                    + (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(2) / 8U) 
#endif
#ifdef RTE_USB_Device_CustomClass_3
                                                    + (USBD_CCn_EP_THREAD_TOTAL_STACK_SIZE(3) / 8U) 
#endif
#endif
] __attribute__((section(".bss.os.thread.stack")));
#endif

static const thread_sp_t usbd_cc_ep_thread_sp      [USBD_CUSTOM_CLASS_NUM * USBD_EP_NUM] = {
#ifdef RTE_USB_Device_CustomClass_0
    { 0U, osPriorityNone }
#if (USBD_EP_LAST_NUM >=      1)
  , USBD_CCn_EPx_THREAD_SP(0, 1)
#endif
#if (USBD_EP_LAST_NUM >=      2)
  , USBD_CCn_EPx_THREAD_SP(0, 2)
#endif
#if (USBD_EP_LAST_NUM >=      3)
  , USBD_CCn_EPx_THREAD_SP(0, 3)
#endif
#if (USBD_EP_LAST_NUM >=      4)
  , USBD_CCn_EPx_THREAD_SP(0, 4)
#endif
#if (USBD_EP_LAST_NUM >=      5)
  , USBD_CCn_EPx_THREAD_SP(0, 5)
#endif
#if (USBD_EP_LAST_NUM >=      6)
  , USBD_CCn_EPx_THREAD_SP(0, 6)
#endif
#if (USBD_EP_LAST_NUM >=      7)
  , USBD_CCn_EPx_THREAD_SP(0, 7)
#endif
#if (USBD_EP_LAST_NUM >=      8)
  , USBD_CCn_EPx_THREAD_SP(0, 8)
#endif
#if (USBD_EP_LAST_NUM >=      9)
  , USBD_CCn_EPx_THREAD_SP(0, 9)
#endif
#if (USBD_EP_LAST_NUM >=      10)
  , USBD_CCn_EPx_THREAD_SP(0, 10)
#endif
#if (USBD_EP_LAST_NUM >=      11)
  , USBD_CCn_EPx_THREAD_SP(0, 11)
#endif
#if (USBD_EP_LAST_NUM >=      12)
  , USBD_CCn_EPx_THREAD_SP(0, 12)
#endif
#if (USBD_EP_LAST_NUM >=      13)
  , USBD_CCn_EPx_THREAD_SP(0, 13)
#endif
#if (USBD_EP_LAST_NUM >=      14)
  , USBD_CCn_EPx_THREAD_SP(0, 14)
#endif
#if (USBD_EP_LAST_NUM >=      15)
  , USBD_CCn_EPx_THREAD_SP(0, 15)
#endif
#endif
#ifdef RTE_USB_Device_CustomClass_1
  , { 0U, osPriorityNone }
#if (USBD_EP_LAST_NUM >=      1)
  , USBD_CCn_EPx_THREAD_SP(1, 1)
#endif
#if (USBD_EP_LAST_NUM >=      2)
  , USBD_CCn_EPx_THREAD_SP(1, 2)
#endif
#if (USBD_EP_LAST_NUM >=      3)
  , USBD_CCn_EPx_THREAD_SP(1, 3)
#endif
#if (USBD_EP_LAST_NUM >=      4)
  , USBD_CCn_EPx_THREAD_SP(1, 4)
#endif
#if (USBD_EP_LAST_NUM >=      5)
  , USBD_CCn_EPx_THREAD_SP(1, 5)
#endif
#if (USBD_EP_LAST_NUM >=      6)
  , USBD_CCn_EPx_THREAD_SP(1, 6)
#endif
#if (USBD_EP_LAST_NUM >=      7)
  , USBD_CCn_EPx_THREAD_SP(1, 7)
#endif
#if (USBD_EP_LAST_NUM >=      8)
  , USBD_CCn_EPx_THREAD_SP(1, 8)
#endif
#if (USBD_EP_LAST_NUM >=      9)
  , USBD_CCn_EPx_THREAD_SP(1, 9)
#endif
#if (USBD_EP_LAST_NUM >=      10)
  , USBD_CCn_EPx_THREAD_SP(1, 10)
#endif
#if (USBD_EP_LAST_NUM >=      11)
  , USBD_CCn_EPx_THREAD_SP(1, 11)
#endif
#if (USBD_EP_LAST_NUM >=      12)
  , USBD_CCn_EPx_THREAD_SP(1, 12)
#endif
#if (USBD_EP_LAST_NUM >=      13)
  , USBD_CCn_EPx_THREAD_SP(1, 13)
#endif
#if (USBD_EP_LAST_NUM >=      14)
  , USBD_CCn_EPx_THREAD_SP(1, 14)
#endif
#if (USBD_EP_LAST_NUM >=      15)
  , USBD_CCn_EPx_THREAD_SP(1, 15)
#endif
#endif
#ifdef RTE_USB_Device_CustomClass_2
  , { 0U, osPriorityNone }
#if (USBD_EP_LAST_NUM >=      1)
  , USBD_CCn_EPx_THREAD_SP(2, 1)
#endif
#if (USBD_EP_LAST_NUM >=      2)
  , USBD_CCn_EPx_THREAD_SP(2, 2)
#endif
#if (USBD_EP_LAST_NUM >=      3)
  , USBD_CCn_EPx_THREAD_SP(2, 3)
#endif
#if (USBD_EP_LAST_NUM >=      4)
  , USBD_CCn_EPx_THREAD_SP(2, 4)
#endif
#if (USBD_EP_LAST_NUM >=      5)
  , USBD_CCn_EPx_THREAD_SP(2, 5)
#endif
#if (USBD_EP_LAST_NUM >=      6)
  , USBD_CCn_EPx_THREAD_SP(2, 6)
#endif
#if (USBD_EP_LAST_NUM >=      7)
  , USBD_CCn_EPx_THREAD_SP(2, 7)
#endif
#if (USBD_EP_LAST_NUM >=      8)
  , USBD_CCn_EPx_THREAD_SP(2, 8)
#endif
#if (USBD_EP_LAST_NUM >=      9)
  , USBD_CCn_EPx_THREAD_SP(2, 9)
#endif
#if (USBD_EP_LAST_NUM >=      10)
  , USBD_CCn_EPx_THREAD_SP(2, 10)
#endif
#if (USBD_EP_LAST_NUM >=      11)
  , USBD_CCn_EPx_THREAD_SP(2, 11)
#endif
#if (USBD_EP_LAST_NUM >=      12)
  , USBD_CCn_EPx_THREAD_SP(2, 12)
#endif
#if (USBD_EP_LAST_NUM >=      13)
  , USBD_CCn_EPx_THREAD_SP(2, 13)
#endif
#if (USBD_EP_LAST_NUM >=      14)
  , USBD_CCn_EPx_THREAD_SP(2, 14)
#endif
#if (USBD_EP_LAST_NUM >=      15)
  , USBD_CCn_EPx_THREAD_SP(2, 15)
#endif
#endif
#ifdef RTE_USB_Device_CustomClass_3
  , { 0U, osPriorityNone }
#if (USBD_EP_LAST_NUM >=      1)
  , USBD_CCn_EPx_THREAD_SP(3, 1)
#endif
#if (USBD_EP_LAST_NUM >=      2)
  , USBD_CCn_EPx_THREAD_SP(3, 2)
#endif
#if (USBD_EP_LAST_NUM >=      3)
  , USBD_CCn_EPx_THREAD_SP(3, 3)
#endif
#if (USBD_EP_LAST_NUM >=      4)
  , USBD_CCn_EPx_THREAD_SP(3, 4)
#endif
#if (USBD_EP_LAST_NUM >=      5)
  , USBD_CCn_EPx_THREAD_SP(3, 5)
#endif
#if (USBD_EP_LAST_NUM >=      6)
  , USBD_CCn_EPx_THREAD_SP(3, 6)
#endif
#if (USBD_EP_LAST_NUM >=      7)
  , USBD_CCn_EPx_THREAD_SP(3, 7)
#endif
#if (USBD_EP_LAST_NUM >=      8)
  , USBD_CCn_EPx_THREAD_SP(3, 8)
#endif
#if (USBD_EP_LAST_NUM >=      9)
  , USBD_CCn_EPx_THREAD_SP(3, 9)
#endif
#if (USBD_EP_LAST_NUM >=      10)
  , USBD_CCn_EPx_THREAD_SP(3, 10)
#endif
#if (USBD_EP_LAST_NUM >=      11)
  , USBD_CCn_EPx_THREAD_SP(3, 11)
#endif
#if (USBD_EP_LAST_NUM >=      12)
  , USBD_CCn_EPx_THREAD_SP(3, 12)
#endif
#if (USBD_EP_LAST_NUM >=      13)
  , USBD_CCn_EPx_THREAD_SP(3, 13)
#endif
#if (USBD_EP_LAST_NUM >=      14)
  , USBD_CCn_EPx_THREAD_SP(3, 14)
#endif
#if (USBD_EP_LAST_NUM >=      15)
  , USBD_CCn_EPx_THREAD_SP(3, 15)
#endif
#endif
};
#endif

#if   (USBD_ADC_NUM > 0)
static const char *usbd_adc_thread_name            [USBD_ADC_NUM] = {
#ifdef RTE_USB_Device_ADC_0
    "USBD_ADC0_Thread"
#endif
#ifdef RTE_USB_Device_ADC_1
  , "USBD_ADC1_Thread"
#endif
#ifdef RTE_USB_Device_ADC_2
  , "USBD_ADC2_Thread"
#endif
#ifdef RTE_USB_Device_ADC_3
  , "USBD_ADC3_Thread"
#endif
};

#ifdef RTE_USB_Device_ADC_0
static osRtxThread_t      usbd_adc_thread_cb_mem   [USBD_ADC_NUM] __attribute__((section(".bss.os.thread.cb")));
static uint64_t           usbd_adc_thread_stack_mem[
#ifdef RTE_USB_Device_ADC_0
                                                   ((USBD_ADC0_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_ADC_1
                                                 + ((USBD_ADC1_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_ADC_2
                                                 + ((USBD_ADC2_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_ADC_3
                                                 + ((USBD_ADC3_THREAD_STACK_SIZE + 7U) / 8U)
#endif
] __attribute__((section(".bss.os.thread.stack")));
#endif

static const thread_sp_t  usbd_adc_thread_sp       [USBD_ADC_NUM] = {
#ifdef RTE_USB_Device_ADC_0
    { ((USBD_ADC0_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_ADC0_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_ADC_1
  , { ((USBD_ADC1_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_ADC1_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_ADC_2
  , { ((USBD_ADC2_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_ADC2_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_ADC_3
  , { ((USBD_ADC3_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_ADC3_THREAD_PRIORITY }
#endif
};
#endif

#if   (USBD_CDC_NUM > 0)
static const char *usbd_cdc_thread_name            [USBD_CDC_NUM * 2U] = {
#ifdef RTE_USB_Device_CDC_0
    "USBD_CDC0_Int_Thread"
  , "USBD_CDC0_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_1
  , "USBD_CDC1_Int_Thread"
  , "USBD_CDC1_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_2
  , "USBD_CDC2_Int_Thread"
  , "USBD_CDC2_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_3
  , "USBD_CDC3_Int_Thread"
  , "USBD_CDC3_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_4
  , "USBD_CDC4_Int_Thread"
  , "USBD_CDC4_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_5
  , "USBD_CDC5_Int_Thread"
  , "USBD_CDC5_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_6
  , "USBD_CDC6_Int_Thread"
  , "USBD_CDC6_Bulk_Thread"
#endif
#ifdef RTE_USB_Device_CDC_7
  , "USBD_CDC7_Int_Thread"
  , "USBD_CDC7_Bulk_Thread"
#endif
};

#ifdef RTE_CMSIS_RTOS2_RTX5
static osRtxThread_t      usbd_cdc_thread_cb_mem   [USBD_CDC_NUM * 2U] __attribute__((section(".bss.os.thread.cb")));
static uint64_t           usbd_cdc_thread_stack_mem[
#ifdef RTE_USB_Device_CDC_0
                                                   ((USBD_CDC0_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC0_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_1
                                                 + ((USBD_CDC1_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC1_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_2
                                                 + ((USBD_CDC2_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC2_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_3
                                                 + ((USBD_CDC3_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC3_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_4
                                                 + ((USBD_CDC4_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC4_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_5
                                                 + ((USBD_CDC5_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC5_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_6
                                                 + ((USBD_CDC6_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC6_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_CDC_7
                                                 + ((USBD_CDC7_INT_THREAD_STACK_SIZE  + 7U) / 8U) + ((USBD_CDC7_BULK_THREAD_STACK_SIZE + 7U) / 8U)
#endif
] __attribute__((section(".bss.os.thread.stack")));
#endif

static const thread_sp_t  usbd_cdc_thread_sp       [USBD_CDC_NUM * 2U] = {
#ifdef RTE_USB_Device_CDC_0
    { ((USBD_CDC0_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC0_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC0_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC0_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_1
  , { ((USBD_CDC1_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC1_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC1_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC1_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_2
  , { ((USBD_CDC2_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC2_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC2_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC2_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_3
  , { ((USBD_CDC3_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC3_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC3_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC3_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_4
  , { ((USBD_CDC4_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC4_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC4_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC4_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_5
  , { ((USBD_CDC5_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC5_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC5_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC5_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_6
  , { ((USBD_CDC6_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC6_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC6_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC6_BULK_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_CDC_7
  , { ((USBD_CDC7_INT_THREAD_STACK_SIZE  + 7U) / 8U) * 8U, USBD_CDC7_INT_THREAD_PRIORITY  }
  , { ((USBD_CDC7_BULK_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_CDC7_BULK_THREAD_PRIORITY }
#endif
};
#endif

#if   (USBD_HID_NUM > 0)
static const char *usbd_hid_thread_name            [USBD_HID_NUM] = {
#ifdef RTE_USB_Device_HID_0
    "USBD_HID0_Thread"
#endif
#ifdef RTE_USB_Device_HID_1
  , "USBD_HID1_Thread"
#endif
#ifdef RTE_USB_Device_HID_2
  , "USBD_HID2_Thread"
#endif
#ifdef RTE_USB_Device_HID_3
  , "USBD_HID3_Thread"
#endif
#ifdef RTE_USB_Device_HID_4
  , "USBD_HID4_Thread"
#endif
#ifdef RTE_USB_Device_HID_5
  , "USBD_HID5_Thread"
#endif
#ifdef RTE_USB_Device_HID_6
  , "USBD_HID6_Thread"
#endif
#ifdef RTE_USB_Device_HID_7
  , "USBD_HID7_Thread"
#endif
};

#ifdef RTE_CMSIS_RTOS2_RTX5
static osRtxThread_t      usbd_hid_thread_cb_mem   [USBD_HID_NUM] __attribute__((section(".bss.os.thread.cb")));
static uint64_t           usbd_hid_thread_stack_mem[
#ifdef RTE_USB_Device_HID_0
                                                   ((USBD_HID0_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_1
                                                 + ((USBD_HID1_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_2
                                                 + ((USBD_HID2_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_3
                                                 + ((USBD_HID3_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_4
                                                 + ((USBD_HID4_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_5
                                                 + ((USBD_HID5_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_6
                                                 + ((USBD_HID6_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_HID_7
                                                 + ((USBD_HID7_THREAD_STACK_SIZE + 7U) / 8U)
#endif
] __attribute__((section(".bss.os.thread.stack")));
#endif

static const thread_sp_t  usbd_hid_thread_sp       [USBD_HID_NUM] = {
#ifdef RTE_USB_Device_HID_0
    { ((USBD_HID0_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID0_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_1
  , { ((USBD_HID1_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID1_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_2
  , { ((USBD_HID2_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID2_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_3
  , { ((USBD_HID3_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID3_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_4
  , { ((USBD_HID4_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID4_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_5
  , { ((USBD_HID5_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID5_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_6
  , { ((USBD_HID6_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID6_THREAD_PRIORITY }
#endif
#ifdef RTE_USB_Device_HID_7
  , { ((USBD_HID7_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_HID7_THREAD_PRIORITY }
#endif
};
#endif

#if   (USBD_MSC_NUM > 0)
static const char *usbd_msc_thread_name            [USBD_MSC_NUM] = {
#ifdef    RTE_USB_Device_MSC_0
    "USBD_MSC0_Thread"
#endif
#ifdef    RTE_USB_Device_MSC_1
  , "USBD_MSC1_Thread"
#endif
#ifdef    RTE_USB_Device_MSC_2
  , "USBD_MSC2_Thread"
#endif
#ifdef    RTE_USB_Device_MSC_3
  , "USBD_MSC3_Thread"
#endif
};

#ifdef RTE_CMSIS_RTOS2_RTX5
static osRtxThread_t      usbd_msc_thread_cb_mem   [USBD_MSC_NUM] __attribute__((section(".bss.os.thread.cb")));
static uint64_t           usbd_msc_thread_stack_mem[
#ifdef RTE_USB_Device_MSC_0
                                                   ((USBD_MSC0_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_MSC_1
                                                 + ((USBD_MSC1_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_MSC_2
                                                 + ((USBD_MSC2_THREAD_STACK_SIZE + 7U) / 8U)
#endif
#ifdef RTE_USB_Device_MSC_3
                                                 + ((USBD_MSC3_THREAD_STACK_SIZE + 7U) / 8U)
#endif
] __attribute__((section(".bss.os.thread.stack")));
#endif

static const thread_sp_t  usbd_msc_thread_sp       [USBD_MSC_NUM] = {
#ifdef    RTE_USB_Device_MSC_0
    { ((USBD_MSC0_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_MSC0_THREAD_PRIORITY }
#endif
#ifdef    RTE_USB_Device_MSC_1
  , { ((USBD_MSC1_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_MSC1_THREAD_PRIORITY }
#endif
#ifdef    RTE_USB_Device_MSC_2
  , { ((USBD_MSC2_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_MSC2_THREAD_PRIORITY }
#endif
#ifdef    RTE_USB_Device_MSC_3
  , { ((USBD_MSC3_THREAD_STACK_SIZE + 7U) / 8U) * 8U, USBD_MSC3_THREAD_PRIORITY }
#endif
};
#endif


// Create timer definitions
#if ((USBD_HID_NUM > 0) && defined(RTE_CMSIS_RTOS2_RTX5))
static osRtxTimer_t     usbd_hid_timer_cb_mem          [USBD_HID_NUM]                   __attribute__((section(".bss.os.timer.cb")));
#endif


// Create semaphore definitions
#ifdef RTE_CMSIS_RTOS2_RTX5
static osRtxSemaphore_t usbd_driver_semaphore_cb_mem   [USBD_DEV_NUM]                   __attribute__((section(".bss.os.semaphore.cb")));
static osRtxSemaphore_t usbd_driver_ep_semaphore_cb_mem[USBD_DEV_NUM][USBD_EP_NUM * 2U] __attribute__((section(".bss.os.semaphore.cb")));
#if   (USBD_HID_NUM > 0)
static osRtxSemaphore_t usbd_hid_semaphore_cb_mem      [USBD_HID_NUM]                   __attribute__((section(".bss.os.semaphore.cb")));
#endif
#endif

// Functions definition

/// \brief Create a thread
/// \param[in]     thread               thread
/// \param[in]     index                parameter dependent on thread (device index or class instance or endpoint index)
/// \return
///                value != 0:          handle to created thread
///                value = 0:           thread creation failed
void *USBD_ThreadCreate (usbdThread_t thread, uint8_t index) {
#if (((USBD_CUSTOM_CLASS_NUM > 0) || \
      (USBD_ADC_NUM          > 0) || \
      (USBD_CDC_NUM          > 0) || \
      (USBD_HID_NUM          > 0) || \
      (USBD_MSC_NUM          > 0)) )
  osThreadAttr_t thread_attr;
#ifdef RTE_CMSIS_RTOS2_RTX5
  uint32_t       i, sum;
#endif

  memset(&thread_attr, 0U, sizeof(osThreadAttr_t));
#endif

  switch (thread) {
    case usbdThreadCore:
      if (index >= USBD_DEV_NUM) { return NULL; }
      return ((void *)osThreadNew (USBD_Core_Thread, (void *)((uint32_t)index), usbd_core_thread_attr[index]));

    case usbdThreadCC:
#if (USBD_CUSTOM_CLASS_NUM > 0)
      if (index >= (USBD_CUSTOM_CLASS_NUM * USBD_EP_NUM)) { return NULL; }
      thread_attr.name       = usbd_cc_ep_thread_name[index];
#ifdef RTE_CMSIS_RTOS2_RTX5
      thread_attr.cb_mem     = &usbd_cc_ep_thread_cb_mem[index];
      thread_attr.cb_size    =  sizeof(osRtxThread_t);
      sum = 0U;
      for (i = 0U; i < index; i++) {
        sum += usbd_cc_ep_thread_sp[i].stack_size;
      }
      thread_attr.stack_mem  = &usbd_cc_ep_thread_stack_mem[sum / 8U];
#endif
      thread_attr.stack_size =  usbd_cc_ep_thread_sp[index].stack_size;
      thread_attr.priority   =  usbd_cc_ep_thread_sp[index].priority;
      return ((void *)osThreadNew (USBD_CustomClass_EP_Thread, (void *)((uint32_t)index / USBD_EP_NUM) , &thread_attr));
#else
      return NULL;
#endif

    case usbdThreadADC:
#if (USBD_ADC_NUM > 0)
      if (index >= USBD_ADC_NUM) { return NULL; }
      thread_attr.name       = usbd_adc_thread_name[index];
#ifdef RTE_CMSIS_RTOS2_RTX5
      thread_attr.cb_mem     = &usbd_adc_thread_cb_mem[index];
      thread_attr.cb_size    =  sizeof(osRtxThread_t);
      sum = 0U;
      for (i = 0U; i < index; i++) {
        sum += usbd_adc_thread_sp[i].stack_size;
      }
      thread_attr.stack_mem  = &usbd_adc_thread_stack_mem[sum / 8U];
#endif
      thread_attr.stack_size =  usbd_adc_thread_sp[index].stack_size;
      thread_attr.priority   =  usbd_adc_thread_sp[index].priority;
      return ((void *)osThreadNew (USBD_ADC_Thread, (void *)((uint32_t)index), &thread_attr));
#else
      return NULL;
#endif

    case usbdThreadCDC_Int:
#if (USBD_CDC_NUM > 0)
      if (index >= USBD_CDC_NUM) { return NULL; }
      thread_attr.name       = usbd_cdc_thread_name[index * 2U];
#ifdef RTE_CMSIS_RTOS2_RTX5
      thread_attr.cb_mem     = &usbd_cdc_thread_cb_mem[index * 2U];
      thread_attr.cb_size    =  sizeof(osRtxThread_t);
      sum = 0U;
      for (i = 0U; i < (index * 2U); i++) {
        sum += usbd_cdc_thread_sp[i].stack_size;
      }
      thread_attr.stack_mem  = &usbd_cdc_thread_stack_mem[sum / 8U];
#endif
      thread_attr.stack_size =  usbd_cdc_thread_sp[index * 2U].stack_size;
      thread_attr.priority   =  usbd_cdc_thread_sp[index * 2U].priority;
      return ((void *)osThreadNew (USBD_CDC_Int_Thread, (void *)((uint32_t)index), &thread_attr));
#else
      return NULL;
#endif
    case usbdThreadCDC_Bulk:
#if (USBD_CDC_NUM > 0)
      if (index >= USBD_CDC_NUM) { return NULL; }
      thread_attr.name       = usbd_cdc_thread_name[(index * 2U) + 1U];
#ifdef RTE_CMSIS_RTOS2_RTX5
      thread_attr.cb_mem     = &usbd_cdc_thread_cb_mem[(index * 2U) + 1U];
      thread_attr.cb_size    =  sizeof(osRtxThread_t);
      sum = 0U;
      for (i = 0U; i < ((index *2U) + 1U); i++) {
        sum += usbd_cdc_thread_sp[i].stack_size;
      }
      thread_attr.stack_mem  = &usbd_cdc_thread_stack_mem[sum / 8U];
#endif
      thread_attr.stack_size =  usbd_cdc_thread_sp[(index * 2U) + 1U].stack_size;
      thread_attr.priority   =  usbd_cdc_thread_sp[(index * 2U) + 1U].priority;
      return ((void *)osThreadNew (USBD_CDC_Bulk_Thread, (void *)((uint32_t)index), &thread_attr));
#else
      return NULL;
#endif

    case usbdThreadHID:
#if (USBD_HID_NUM > 0)
      if (index >= USBD_HID_NUM) { return NULL; }
      thread_attr.name       = usbd_hid_thread_name[index];
#ifdef RTE_CMSIS_RTOS2_RTX5
      thread_attr.cb_mem     = &usbd_hid_thread_cb_mem[index];
      thread_attr.cb_size    =  sizeof(osRtxThread_t);
      sum = 0U;
      for (i = 0U; i < index; i++) {
        sum += usbd_hid_thread_sp[i].stack_size;
      }
      thread_attr.stack_mem  = &usbd_hid_thread_stack_mem[sum / 8U];
#endif
      thread_attr.stack_size =  usbd_hid_thread_sp[index].stack_size;
      thread_attr.priority   =  usbd_hid_thread_sp[index].priority;
      return ((void *)osThreadNew (USBD_HID_Thread, (void *)((uint32_t)index), &thread_attr));
#else
      return NULL;
#endif

    case usbdThreadMSC:
#if (USBD_MSC_NUM > 0)
      if (index >= USBD_MSC_NUM) { return NULL; }
      thread_attr.name       = usbd_msc_thread_name[index];
#ifdef RTE_CMSIS_RTOS2_RTX5
      thread_attr.cb_mem     = &usbd_msc_thread_cb_mem[index];
      thread_attr.cb_size    =  sizeof(osRtxThread_t);
      sum = 0U;
      for (i = 0U; i < index; i++) {
        sum += usbd_msc_thread_sp[i].stack_size;
      }
      thread_attr.stack_mem  = &usbd_msc_thread_stack_mem[sum / 8U];
#endif
      thread_attr.stack_size =  usbd_msc_thread_sp[index].stack_size;
      thread_attr.priority   =  usbd_msc_thread_sp[index].priority;
      return ((void *)osThreadNew (USBD_MSC_Thread, (void *)((uint32_t)index), &thread_attr));
#else
      return NULL;
#endif
  }
  return NULL;
}

/// \brief Terminate execution of a thread
/// \param[in]     thread_hndl          thread handle
/// \return
///                value 0:             thread terminated successfully
///                value < 0:           thread termination failed
int32_t USBD_ThreadTerminate (void *thread_hndl) {
  if (thread_hndl == NULL) { return -1; }
  return (osThreadTerminate ((osThreadId_t)thread_hndl));
}

/// \brief Helper function to convert ms to ticks
/// \param[in]     ms                   number of milliseconds
/// \return        ticks
static uint32_t USBD_MsToTick (uint32_t ms) {
  static uint32_t tick_freq_mul = 0U;
  static uint32_t max_ms;

  if (tick_freq_mul == 0U) {
    // Initialize helper variables once
    tick_freq_mul = (osKernelGetTickFreq () * 1024U) / 1000U;
    max_ms        = (0xFFFFFFFFU - 1023U) / tick_freq_mul;
  }

  if ((ms == 0U) || (ms == osWaitForever) || (tick_freq_mul == 1024U)) {
    return ms;
  }

  if (ms > max_ms) {
    return (osWaitForever - 1U);
  }

  return (((ms * tick_freq_mul) + 1023U) / 1024U);
}

/// \brief Delay execution of a thread for specified number of milliseconds
/// \param[in]     millisec             number of milliseconds
/// \return
///                value 0:             delay finished
///                value < 0:           delay failed
int32_t USBD_Delay (uint32_t millisec) {
  return  (osDelay (USBD_MsToTick(millisec)));
}

/// \brief Create and initialize a periodic timer for HID class handling
/// \param[in]     instance             class instance
/// \return
///                value != 0:          handle to created timer
///                value = 0:           timer creation failed
void *USBD_TimerCreate (uint8_t instance) {
#if (USBD_HID_NUM > 0)
  osTimerAttr_t timer_attr;

  if (instance >= USBD_HID_NUM) { return NULL; }
  memset(&timer_attr, 0U, sizeof(osTimerAttr_t));
#ifdef RTE_CMSIS_RTOS2_RTX5
  timer_attr.cb_mem  = &usbd_hid_timer_cb_mem[instance];
  timer_attr.cb_size =  sizeof(osRtxTimer_t);
#endif
  return ((void *)osTimerNew ((osTimerFunc_t)USBD_HID_Timer, osTimerPeriodic, (void *)((uint32_t)instance), &timer_attr));
#else
  (void)instance;
  return NULL;
#endif
}

/// \brief Start or restart a timer
/// \param[in]     timer_hndl           timer handle
/// \return
///                value 0:             timer started or restarted successfully
///                value < 0:           timer start or restart failed
int32_t USBD_TimerStart (void *timer_hndl, uint32_t millisec) {
  if (timer_hndl == NULL) { return -1; }
  return  (osTimerStart ((osTimerId_t)timer_hndl, USBD_MsToTick(millisec)));
}

/// \brief Stop a timer
/// \param[in]     timer_hndl           timer handle
/// \return
///                value 0:             timer stopped successfully
///                value < 0:           timer stop failed
int32_t USBD_TimerStop (void *timer_hndl) {
  if (timer_hndl == NULL) { return -1; }
  if (osTimerIsRunning((osTimerId_t)timer_hndl) == 0) {
    return 0;
  }
  return (osTimerStop ((osTimerId_t)timer_hndl));
}

/// \brief Delete a timer
/// \param[in]     timer_hndl           timer handle
/// \return
///                value 0:             timer deleted successfully
///                value < 0:           timer deletion failed
int32_t USBD_TimerDelete (void *timer_hndl) {
  if (timer_hndl == NULL) { return -1; }
  return (osTimerDelete ((osTimerId_t)timer_hndl));
}

/// \brief Set the specified flags of a thread
/// \param[in]     thread_hndl          thread handle
/// \param[in]     flags                flags to be set
/// \return
///                value 0:             flags set successfully
///                value >= 0x80000000: setting of flags failed
uint32_t USBD_ThreadFlagsSet (void *thread_hndl, uint32_t flags) {
  if (thread_hndl == NULL) { return 0U; }
  return ((uint32_t)osThreadFlagsSet ((osThreadId_t)thread_hndl, flags));
}

/// \brief Wait for any USB related flag of currently running thread to become signaled
/// \param[in]     millisec             time-out in milliseconds, or 0 in case of no time-out
/// \return
///                value < 0x80000000:  flags
///                value 0:             time-out
///                value >= 0x80000000: error
uint32_t USBD_ThreadFlagsWait (uint32_t millisec) {
  uint32_t res;

  res = osThreadFlagsWait (0xFFFFU, osFlagsWaitAny, USBD_MsToTick(millisec));
  if ((res & 0x80000000U) == 0U) {
    return res;
  } else if (res == (uint32_t)osErrorTimeout) {
    return 0U;
  } else {
    return 0x80000000U;
  }
}

/// \brief Create and initialize a binary semaphore
/// \param[in]     semaphore            semaphore
/// \param[in]     index1               semaphore dependent index1 (device index or class instance)
/// \param[in]     index2               semaphore dependent index2 (endpoint index)
/// \return
///                value != 0:          handle to created semaphore
///                value = 0:           semaphore creation failed
void *USBD_SemaphoreCreate (usbdSemaphore_t semaphore, uint8_t index1, uint8_t index2) {
  osSemaphoreAttr_t semaphore_attr;

  memset(&semaphore_attr, 0U, sizeof(osSemaphoreAttr_t));

  switch (semaphore) {
    case usbdSemaphoreCore:
      if (index1 >= USBD_DEV_NUM) { return NULL; }
#ifdef RTE_CMSIS_RTOS2_RTX5
      semaphore_attr.cb_mem  = &usbd_driver_semaphore_cb_mem[index1];
      semaphore_attr.cb_size =  sizeof(osRtxSemaphore_t);
#endif
      return ((void *)osSemaphoreNew (1U, 1U, &semaphore_attr));

    case usbdSemaphoreEndpoint:
      if (index1 >=  USBD_DEV_NUM)      { return NULL; }
      if (index2 >= (USBD_EP_NUM * 2U)) { return NULL; }
#ifdef RTE_CMSIS_RTOS2_RTX5
      semaphore_attr.cb_mem  = &usbd_driver_ep_semaphore_cb_mem[index1][index2];
      semaphore_attr.cb_size =  sizeof(osRtxSemaphore_t);
#endif
      return ((void *)osSemaphoreNew (1U, 1U, &semaphore_attr));

    case usbdSemaphoreHID:
#if (USBD_HID_NUM > 0)
      if (index1 >= USBD_HID_NUM) { return NULL; }
#ifdef RTE_CMSIS_RTOS2_RTX5
      semaphore_attr.cb_mem  = &usbd_hid_semaphore_cb_mem[index1];
      semaphore_attr.cb_size =  sizeof(osRtxSemaphore_t);
#endif
      return ((void *)osSemaphoreNew (1U, 1U, &semaphore_attr));
#else
      return NULL;
#endif
  }
  return NULL;
}

/// \brief Wait for a semaphore token to become available and acquire it
/// \param[in]     semaphore_hndl       semaphore handle
/// \param[in]     millisec             time-out in milliseconds, or 0 in case of no time-out
/// \return
///                value 0:             token acquired successfully
///                value < 0:           token acquire failed
int32_t USBD_SemaphoreAcquire (void *semaphore_hndl, uint32_t millisec) {
  if (semaphore_hndl == NULL) { return -1; }
  return (osSemaphoreAcquire ((osSemaphoreId_t)semaphore_hndl, USBD_MsToTick(millisec)));
}

/// \brief Release a semaphore token
/// \param[in]     semaphore_hndl       semaphore handle
/// \return
///                value 0:             token released successfully
///                value < 0:           token release failed
int32_t USBD_SemaphoreRelease (void *semaphore_hndl) {
  if (semaphore_hndl == NULL) { return -1; }
  return (osSemaphoreRelease ((osSemaphoreId_t)semaphore_hndl));
}

/// \brief Delete a semaphore
/// \param[in]     semaphore_hndl       semaphore handle
/// \return
///                value 0:             semaphore deleted successfully
///                value < 0:           semaphore deletion failed
int32_t USBD_SemaphoreDelete (void *semaphore_hndl) {
  if (semaphore_hndl == NULL) { return -1; }
  return (osSemaphoreDelete ((osSemaphoreId_t)semaphore_hndl));
}
